﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using Elderos.Utils.Logging;

namespace Elderos.AI
{
    [DataContract]
    [Serializable]
    public class Normalizer : INormalizer
    {
        [DataMember]
        public double[] MaxThresholds { get; private set; }

        [DataMember]
        public double[] MinThresholds { get; private set; }

        public Normalizer(double[][] inputs)
        {
            var reversed = new List<double>[inputs.First().Length];
            for (int i = 0; i < reversed.Length; i++)
            {
                reversed[i] = new List<double>();
            }

            foreach (double[] doubles in inputs)
            {
                for (int i = 0; i < doubles.Length; i++)
                {
                    reversed[i].Add(doubles[i]);
                }
            }

            MaxThresholds = new double[reversed.Length];
            MinThresholds = new double[reversed.Length];
            int size = reversed[0].Count - reversed[0].Count / 800;
            for (int i = 0; i < reversed.Length; i++)
            {
                double min = reversed[i].OrderByDescending(x => x).Take(size).Last();
                double max = reversed[i].OrderBy(x => x).Take(size).Last();
                MaxThresholds[i] = max;
                MinThresholds[i] = min;
                if (min >= max)
                {
                    Logger.Warn("Vector dimension no " + i + " has only one value after normalization.");
                    if (reversed[i].Distinct().Count() < 2)
                        Logger.Warn("The only value was present in list.");
                    else
                    {
                        var distribution = reversed[i].GroupBy(x => x).ToDictionary(x => x.Key, x => x.Count()).OrderBy(x => x.Key);
                        foreach (var kv in distribution)
                            Logger.Debug("Value: \t" + kv.Key + "\t" + kv.Value);
                    }
                }
            }
        }

        public double[] Normalize(double[] item)
        {
            if (item.Length != MaxThresholds.Length) throw new ArgumentException();

            var result = new double[item.Length];

            for (int i = 0; i < item.Length; i++)
            {
                double x = Math.Max(item[i], MinThresholds[i]);
                x = Math.Min(x, MaxThresholds[i]);
                
                x = (x - MinThresholds[i]) / (MaxThresholds[i] - MinThresholds[i]);

                result[i] = 2*x-1;
            }

            return result;
        }
    }
}
